import json
import re
import traceback

import jsonpath as jsonpath
import requests

from Debug_test import DebugTalk
from common.logger_util import write_log, error_log
from common.yaml_util import read_config_yaml, write_extract_yaml
class RequestsUtil:

    #类变量，通过    请求方式的封装
    session =requests.session()  #获取session对话

    def __init__(self):
        # 读取yaml文件的公共的url数据
        # self.base_url=read_config_yaml(base,base_url)
        self.base_url = ""
        self.last_headers={}

    # 约定请求规范
    def analysis_yaml(self,caseinfo):

        try:
            caseinfo_keys=dict(caseinfo).keys()
            # 判断yaml文件中是否有一级关键字
            if 'name' in caseinfo_keys and 'base_url' in caseinfo_keys and'request' in caseinfo_keys and 'validate' in caseinfo_keys:
                # 判断是否有二级关键字
                request_keys=dict(caseinfo['request']).keys()
                if  'method' in request_keys and 'url' in request_keys:

                    self.base_url=caseinfo['base_url']

                    # 请求头和参数不能去约定
                    method=caseinfo['request']['method']
                    url=caseinfo['request']['url']
                    name=caseinfo['name']

                    headers=None
                    if jsonpath.jsonpath(caseinfo,'$..headers'):
                        headers=caseinfo['request']['headers']
                        del caseinfo['request']['headers']

                    files = None
                    if jsonpath.jsonpath(caseinfo,'$..files'):
                        files = caseinfo['request']['files']
                        for key,value in dict(files).items():
                            files[key] = open(value,'rb')
                        # files=open(caseinfo['request']['files'],'rb')
                        del caseinfo['request']['files']

                    # 我们应该把method，url headers，files，从caseinfo中去掉后把剩余的参数传给kwargs

                    res=self.send_request(name=name,method=caseinfo['request'].pop('method'),
                                      url=caseinfo['request'].pop('url'),
                                      headers=headers,
                                      files=files,
                                      **caseinfo['request'])
                    # 返回json的数据方式方便提取,既要支持正则表达式，有要支持json
                    return_data=res.json()  #前提是要返回json数据
                    return_text=res.text

                    # 输出请求的响应数据
                    # print("===================输出请求的响应数据==========================")
                    # print(return_text)
                    status_code=res.status_code

                    # 提取接口关联的变量,结果关联的话必须使用一级关键字extract
                    if 'extract' in caseinfo_keys:
                        for key,value in dict(caseinfo['extract']).items():
                            # 正则提取
                            if '(.+?)' in value or '(.*?)' in value:
                                ze_value=re.search(value,return_text)
                                # print(ze_value)
                                if ze_value:
                                    extract_data = {key:ze_value.group(1)}
                                    write_extract_yaml(extract_data)
                            else: #json的提取
                                extract_data = {key:return_data[value]}
                                write_extract_yaml(extract_data)
                    # 调用断言的方法封装  提起之后再所断言
                    # 逾期结果，实际结果
                    yq_result = caseinfo['validate']
                    # # 将预期结果和实际结果传到断言里面
                    self.validata_result(yq_result, return_data, status_code)

                else:
                    error_log("一级关键字下必须要有二级级关键字method,url")
            else:
                error_log("必须要有四个一级关键字name,base_url,request,validate")
        except Exception as f:
            error_log("分析yaml文件报错：异常信息：%s" % str(traceback.format_exc()))

    # 替换值方式 通过{{.....}}：
    # def replace_value(self,data):
    #     # 不管是什么类型的数据，通用转换成str的形式
    #     if data and isinstance(data,dict):
    #         str_data = json.dumps(data)
    #     else:
    #         str_data = data
    #
    #     # 替换值
    #     for a in range(1, str_data.count('{{') + 1):
    #         if "{{" in str_data and "}}" in str_data:
    #             start_index = str_data.index("{{")
    #             end_index = str_data.index("}}",start_index) #从start_index坐标开始找下一个结束的}}
    #             old_value = str_data[start_index:end_index + 2]
    #             # 旧的值替换成新的值，从read_extract_yaml里面读取的
    #             new_value = read_extract_yaml(old_value[2:-2])
    #
    #             # 两替换的值必须是str，否则报错
    #             str_data = str_data.replace(old_value, new_value)
    #             # print(str_data)
    #
    #     #还原数据类型
    #     if data and isinstance(data,dict):
    #         data = json.loads(str_data)
    #     else:
    #         data = str_data
    #     # 返回值
    #     return data


    # 热加载 替换值方式${...}
    def replace_load(self,data):

        # 不管是什么类型的数据，通用转换成str的形式
        if data and isinstance(data,dict):
            str_data = json.dumps(data)
        else:
            str_data = data

        # 替换值方式${...}
        for a in range(1, str_data.count("${") + 1):
            if "${" in str_data and "}" in str_data:
                start_index = str_data.index("${")
                end_index = str_data.index("}",start_index) #从start_index坐标开始找下一个结束的}}
                old_value = str_data[start_index:end_index + 1]

                function_name=old_value[2:old_value.index('(')]
                args_values=old_value[old_value.index('(')+1:old_value.index(')')]

                # 反射(通过一个函数的字符串直接去调用设个方法)  通过get_random_number调用DebugTalk
                # new_value = getattr(DebugTalk(), "get_random_number")(10000,99999)
                new_value=getattr(DebugTalk(),function_name)(*args_values.split(',')) # *号的意思是解包，及时把[] 去掉
                # print(new_value)
                # 两替换的值必须是str，否则报错
                str_data = str_data.replace(old_value, str(new_value))
                # print(str_data)

        #还原数据类型
        if data and isinstance(data,dict):
            data = json.loads(str_data)
        else:
            data = str_data
        return data

    # replace_value使用的是{{}}发送请求，如果改成热加载的方式，只需要把replace_value改成 replace_load
    def send_request(self,name,method,url,headers=None,file=None,**kwargs):
        try:
            # 处理method 转换成小写
            self.last_method = str(method).lower()

            # 处理基础路径
            self.base_url=self.replace_load(self.base_url)+self.replace_load(url)

            # print(self.base_url)

            # 处理请求头
            if headers and isinstance(headers,dict):
                self.last_headers=self.replace_load(headers)


            # 核心请求数据去如何替换  肯是params，data，json  这个是处理大括号、{{}}
            for key,value in kwargs.items():
                if key in ['params','data','json']:
                    # 替换{{}}格式数据
                    # value = self.replace_value(value)
                    # 替换${}格式数据 热加载
                    # value = self.replace_load(value)
                    # kwargs[key]=value

                    kwargs[key]=self.replace_load(value)
                    # print("====================请求的参数=============================")
                    # print(kwargs[key])

            # 收集日志
            write_log("\n=================接口请求开始=====================")
            write_log("接口名称：%s"%name)
            write_log("请求方式： %s" %self.last_method)
            write_log("请求路径： %s" %self.base_url)
            write_log("请求header %s" %self.last_headers)
            if 'params' in kwargs.keys():
                write_log("请求参数：%s" %kwargs['params'])
            elif 'data' in kwargs.keys():
                write_log("请求参数：%s" % kwargs['data'])
            elif 'json' in kwargs.keys():
                write_log("请求参数：%s" % kwargs['json'])
            write_log("文件上传：%s" %file)


            # 发送请求
            rep = RequestsUtil.session.request(method=method,url=self.base_url, headers=self.last_headers, **kwargs)
            return rep
        except Exception as f:
            error_log("发送请求报错：异常信息：%s" % str(traceback.format_exc()))

    def validata_result(self,yq_result,sj_result,status_code):
        try:
            write_log("逾期结果：%s" %yq_result)
            write_log("实际结果：%s"%sj_result)
            # 断言是否成功的标记 0 成功 其他的都是失败
            flag =0
            # 解析
            if yq_result and isinstance(yq_result,list):

                for yq in yq_result:
                   for key,value in dict(yq).items():
                       if key == "equals":
                           for assert_key,assert_value in dict(value).items():
                               # yaml里面 key和value
                               # print(assert_key,assert_value)

                               if assert_key=="status_code":
                                   if status_code != assert_value:
                                       flag=flag+1
                                       error_log("断言失败"+assert_key+"不等于"+str(assert_value))
                               else:
                                   key_list=jsonpath.jsonpath(sj_result,'$..%s'%assert_key)
                                   if key_list:
                                       if assert_value not in key_list:
                                           flag=flag+1
                                           error_log("断言失败："+assert_key+"不等于"+str(assert_value))
                                   else:
                                       flag=flag+1
                                       error_log("断言失败：返回结果中不存在："+assert_key)
                       elif key =="contains":
                           # print(value)
                           if value not in json.dumps(sj_result):
                               flag = flag + 1
                               error_log("断言失败：返回结果中不包含：" + value)
                       else:
                           error_log('框架不支持次断言方式:'+key+",需要次断言，联系admin")
            assert flag == 0
            write_log("接口请求成功")
            write_log("\n=================接口请求结束=====================\n")
        except Exception as f:
            write_log("接口请求失败")
            write_log("\n=================接口请求结束=====================\n")
            error_log("断言报错：异常信息：%s" % str(traceback.format_exc()))
# if __name__ == '__main__':
    # url ="https://api.weixin.qq.com/cgi-bin/tags/update?access_token={{access_token}}"
    # for a in range(1, url.count('{{')+1):
    #     if "{{" in url and "}}" in  url:
    #         start_index=url.index("{{")
    #         end_index = url.index("}}")
    #         old_value=url[start_index:end_index+2]
    #         new_value=read_extract_yaml(old_value[2:-2])
    #         # 两替换的值必须是str，否则报错
    #         url=url.replace(old_value,new_value)
    #         print(url)


    # dic={'method': 'get', 'url': '/cgi-bin/token', 'params': {'grant_type': 'client_credential', 'appid': 'wx74a8627810cfa308', 'secret': 'e40a02f9d79a8097df497e6aaf93ab80'}, 'extracr': {'access_token': 'content.access_token'}}
    #
    # print(dic.pop('method'))
    # print(dic)

    # data={"tag": {"id": 134, "name": "广东人${get_random_number(10000,99999)}"}}
    # result= RequestsUtil().replace_load(data)
    # print(result)
